/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "csr_utils.h"
#include "app_schema.h"
#include <mc/exception.h>
#include <mc/format.h>
#include <mc/log.h>
#include <mc/string.h>
#include <regex>
#include <mc/singleton.h>

namespace devmon {

// 常量定义
constexpr std::string_view SYNC_TAG              = "<=/";
constexpr std::string_view REF_TAG               = "#/";
constexpr std::string_view FUNC_CAL_TAG          = "$Func_";
constexpr std::string_view EXPR_TAG              = "|>";
constexpr std::string_view ANCHOR_KEY            = "Anchor";
constexpr std::string_view SYSTEM_ID_KEY         = "SystemId";
constexpr std::string_view PARENT_KEY            = "@Parent";
constexpr std::string_view CHILDREN_KEY          = "@Children";
constexpr std::string_view DEFAULT_KEY           = "@Default";
constexpr int              DEFAULT_LIFE_CYCLE_ID = 1;
constexpr std::string_view NUMERIC_BASETYPES[]   = {"U8", "U16", "U32", "U64",
                                                    "S8", "S16", "S32", "S64"};

bool csr_utils::is_sync(std::string_view str) {
    return mc::string::starts_with(str, SYNC_TAG);
}

bool csr_utils::is_ref(std::string_view str) {
    return mc::string::starts_with(str, REF_TAG);
}

bool csr_utils::is_func_call(std::string_view str) {
    return mc::string::starts_with(str, FUNC_CAL_TAG);
}

bool csr_utils::is_ref_presence(std::string_view prop_value, std::string_view class_name,
                                std::string_view prop_name) {
    return csr_utils::is_ref(prop_value) && class_name == "Connector" && prop_name == "Presence";
}

std::string csr_utils::get_class_name(std::string object_name) {
    auto pos = object_name.find('_');
    if (pos == std::string_view::npos) {
        return object_name;
    }
    return object_name.substr(0, pos);
}

bool csr_utils::is_prop_type_integer(std::string position, std::string object_name,
                                     std::string prop_name) {
    if (prop_name == PARENT_KEY || prop_name == CHILDREN_KEY || prop_name == DEFAULT_KEY) {
        return false;
    }
    auto* device_object =
        object_group_data::get_instance().get_device_object(position, object_name);
    if (device_object != nullptr) {
        return device_object->get_property(prop_name).is_numeric();
    }
    auto class_name = get_class_name(object_name);
    if (class_name == "Func") {
        return false;
    }
    auto prop_type = app_schema::get_instance().get_property_type(class_name, prop_name);
    if (prop_type.empty()) {
        return false;
    }
    return std::find(std::begin(NUMERIC_BASETYPES), std::end(NUMERIC_BASETYPES), prop_type) !=
           std::end(NUMERIC_BASETYPES);
}

std::vector<ref_sync_target> csr_utils::get_sync_targets(std::string prop_value) {
    // TODO: 需要支持表达式2.0的同步语法
    std::vector<ref_sync_target> targets;
    std::regex                   pattern(R"(<=/(:*)(.*?)(?:\[([.\w]+)\])?\.(\w+))");
    std::sregex_iterator         it(prop_value.begin(), prop_value.end(), pattern);
    std::sregex_iterator         end;
    while (it != end) {
        auto match          = *it;
        auto global_tag     = match[1].str();
        auto object_name    = match[2].str();
        auto interface_name = match[3].str();
        auto prop_name      = match[4].str();
        targets.push_back({!global_tag.empty(), object_name, interface_name, prop_name});
        ++it;
    }
    return targets;
}

std::vector<ref_sync_target> csr_utils::get_ref_targets(std::string prop_value) {
    // TODO: 需要支持表达式2.0的引用语法
    std::vector<ref_sync_target> targets;
    std::regex                   pattern(R"(#/(:*)([^.]*)$)");
    std::smatch                  match;
    if (std::regex_match(prop_value, match, pattern)) {
        auto global_tag  = match[1].str();
        auto object_name = match[2].str();
        targets.push_back({!global_tag.empty(), object_name, "", ""});
        return targets;
    }
    pattern = R"(#/(:*)(.*?)(?:\[([.\w]+)\])?\.(\w+))";
    std::sregex_iterator it(prop_value.begin(), prop_value.end(), pattern);
    std::sregex_iterator end;
    while (it != end) {
        auto match          = *it;
        auto global_tag     = match[1].str();
        auto object_name    = match[2].str();
        auto interface_name = match[3].str();
        auto prop_name      = match[4].str();
        targets.push_back({!global_tag.empty(), object_name, interface_name, prop_name});
        ++it;
    }
    return targets;
}

std::string csr_utils::replace_sync_objects(std::string prop_value, string_map_t& objects_map) {
    std::unordered_set<std::string> objects_to_replace;
    for (auto& target : get_sync_targets(prop_value)) {
        if (!target.is_global && objects_map.find(target.object_name) != objects_map.end()) {
            // 先把要替换的object_name加入到哈希集合中，最后再替换，避免同名对象多次替换
            objects_to_replace.insert(target.object_name);
        }
    }
    std::string result = prop_value;
    for (auto& object_name : objects_to_replace) {
        result = mc::string::replace_all(result, std::string(SYNC_TAG) + object_name,
                                         std::string(SYNC_TAG) + objects_map[object_name]);
    }
    return result;
}

std::string csr_utils::replace_ref_object(std::string prop_value, string_map_t& objects_map) {
    std::unordered_set<std::string> objects_to_replace;
    for (auto& target : get_ref_targets(prop_value)) {
        if (!target.is_global && objects_map.find(target.object_name) != objects_map.end()) {
            objects_to_replace.insert(target.object_name);
        }
    }
    std::string result = prop_value;
    for (auto& object_name : objects_to_replace) {
        result = mc::string::replace_all(result, std::string(REF_TAG) + object_name,
                                         std::string(REF_TAG) + objects_map[object_name]);
    }
    return result;
}

std::vector<std::string> csr_utils::get_prop_expr(std::string prop_value, int max_count) {
    std::vector<std::string> exprs;
    std::regex               pattern(R"(\|>(.*)$)");
    std::smatch              match;
    if (!std::regex_search(prop_value, match, pattern)) {
        return exprs;
    }
    auto expr = match[1].str() + std::string(EXPR_TAG);
    pattern   = R"(\s*(.*?)\s*\|>)";
    std::sregex_iterator it(expr.begin(), expr.end(), pattern);
    std::sregex_iterator end;
    while (it != end) {
        auto match = *it;
        exprs.push_back("\"" + match[1].str() + "\"");
        ++it;
    }
    if (max_count > 0 && exprs.size() > max_count) {
        MC_THROW(mc::parse_error_exception, "expression level exceeds limit");
    }
    return exprs;
}

mc::dict csr_utils::convert_topology(const mc::dict& management_topology) {
    mc::mutable_dict         converted_topology;
    std::vector<std::string> item_list = {std::string(ANCHOR_KEY)};
    for (size_t i = 0; i < item_list.size(); ++i) {
        auto item = item_list[i];
        if (!management_topology.contains(item) || !management_topology[item].is_dict()) {
            continue;
        }
        auto item_topology = management_topology[item].as_dict();
        for (auto& entry : item_topology) {
            if (!entry.value.is_array()) {
                continue;
            }
            auto members = entry.value.as<std::vector<std::string>>();
            if (members.empty()) {
                continue;
            }
            std::vector<std::string> topo_members;
            if (converted_topology.contains(item)) {
                topo_members = converted_topology[item].as<std::vector<std::string>>();
            }
            topo_members.insert(topo_members.end(), members.begin(), members.end());
            item_list.insert(item_list.end(), members.begin(), members.end());
            converted_topology[item] = topo_members;
        }
    }
    return converted_topology;
}

instance_info_t csr_utils::get_instance_info(std::string position, std::string object_name,
                                             mc::dict& variable_map) {
    auto* device_object =
        object_group_data::get_instance().get_device_object(position, object_name);
    if (device_object != nullptr) {
        return instance_info_t(device_object);
    }
    auto class_name = get_class_name(object_name);
    return app_schema::get_instance().get_class_info(class_name, variable_map);
}

std::tuple<std::string, std::string>
csr_utils::get_property_intf(std::string position, std::string object_name, std::string prop_name) {
    auto* device_object =
        object_group_data::get_instance().get_device_object(position, object_name);
    if (device_object == nullptr) {
        auto& schema        = app_schema::get_instance();
        auto  class_name    = get_class_name(object_name);
        auto  property_intf = schema.get_property_intf(class_name, prop_name);
        if (property_intf.empty()) {
            return std::make_tuple("", "");
        }
        auto        prop_origin_name = schema.get_property_origin_name(class_name, prop_name);
        std::string property_name    = prop_origin_name.empty() ? prop_name : prop_origin_name;
        return std::make_tuple(property_intf, property_name);
    }

    auto md_info = device_object->get_metadata().get_property_info(prop_name, {});
    if (md_info.interface == nullptr) {
        return std::make_tuple("", "");
    }
    auto* iface = to_interface_ptr(device_object, md_info.interface);
    return std::make_tuple(std::string(iface->get_interface_name()), prop_name);
}

std::tuple<std::string, std::string> csr_utils::get_property_intf(std::string      position,
                                                                  ref_sync_target& target) {
    if (!target.interface_name.empty()) {
        return std::make_tuple(target.interface_name, target.prop_name);
    }
    return get_property_intf(position, target.object_name, target.prop_name);
}

std::string csr_utils::get_connector_position(mc::dict& connector) {
    // 使用GroupPosition而不是Position，因为GroupPosition是对象组的实际位置
    if (!connector.contains("GroupPosition")) {
        return "";
    }
    auto v = connector["GroupPosition"];
    if (v.is_integer()) {
        std::string pos = mc::format_v("%02X", v.as<int>());
        if (pos.size() % 2 != 0) {
            // 如果长度为奇数，则补齐一个0
            pos = "0" + pos;
        }
        return pos;
    }
    return v.as<std::string>();
}

std::string csr_utils::get_local_ref_object_info(std::string_view object_name) {
    return mc::format_dict(R"({{"ObjectName":"${object_name}","type":"local reference object"}})",
                           mc::mutable_dict()("object_name", object_name));
}

std::string csr_utils::get_remote_ref_object_info(std::string_view app, std::string_view path) {
    return mc::format_dict(
        R"({{"Object":{{"Service":"bmc.kepler.${app}","Path":"${path}"}},"type":"remote reference object"}})",
        mc::mutable_dict()("app", app)("path", path));
}

std::string csr_utils::get_local_ref_property_info(std::string_view object_name,
                                                   std::string_view property,
                                                   std::string_view expr) {
    return mc::format_dict(
        R"({{"source":{{"properties":[{{"ObjectName":"${object_name}","Property":"${property}"}}]${expr}}},"type":"local reference property"}})",
        mc::mutable_dict()("object_name", object_name)("property", property)("expr", expr));
}

std::string csr_utils::get_remote_ref_property_info(std::string_view app, std::string_view path,
                                                    std::string_view interface,
                                                    std::string_view property,
                                                    std::string_view expr) {
    return mc::format_dict(
        R"({{"source":{{"properties":[{{"Service":"bmc.kepler.${app}","Path":"${path}","Interface":"${intf}","Property":"${property}"}}]${expr}}},"type":"remote reference property"}})",
        mc::mutable_dict()("app", app)("path", path)("intf", interface)("property",
                                                                        property)("expr", expr));
}

std::string csr_utils::get_sync_property_info(
    std::string_view expr, std::string_view default_str,
    const std::vector<std::tuple<std::string, std::string, std::string, std::string>>& properties) {
    std::vector<std::string> formatted_props;
    for (const auto& [service, path, interface, prop] : properties) {
        formatted_props.push_back(mc::format_dict(
            R"({{"Service":"${service}","Path":"${path}","Interface":"${intf}","Property":"${property}"}})",
            mc::mutable_dict()("service", service)("path", path)("intf", interface)("property", prop)));
    }
    auto props_str = mc::string::join(formatted_props, ",");
    return mc::format_dict(
        R"({{"properties":[${props}]${expr}${default}}})",
        mc::mutable_dict()("props", props_str)("expr", expr)("default", default_str));
}

object_group_data& object_group_data::get_instance() {
    return mc::singleton<object_group_data>::instance();
}

void object_group_data::reset_for_test() {
    mc::singleton<object_group_data>::reset_for_test();
}

object_group_t object_group_data::get_objects(std::string position, std::string owner) {
    std::lock_guard<std::mutex> lock(m_objects_mutex);
    auto                        position_it = m_objects.find(position);
    if (position_it == m_objects.end()) {
        return {};
    }

    auto owner_it = position_it->second.find(owner);
    if (owner_it == position_it->second.end()) {
        return {};
    }

    return owner_it->second;
}

void object_group_data::save_objects(
    std::string position, std::vector<std::shared_ptr<parsed_sr_object>>& parsed_objects,
    std::vector<std::shared_ptr<parsed_sr_func_object>>& parsed_func_objects) {
    auto& device_objects = m_device_objects[position];
    parsed_objects.erase(
        std::remove_if(parsed_objects.begin(), parsed_objects.end(),
                       [&device_objects](const std::shared_ptr<parsed_sr_object>& obj) {
                           return device_objects.find(obj->object_name) != device_objects.end();
                       }),
        parsed_objects.end());
    if (parsed_objects.empty()) {
        ilog("no object groups to save, position: ${position}", ("position", position));
        return;
    }
    std::lock_guard<std::mutex>                     lock(m_objects_mutex);
    std::unordered_map<std::string, object_group_t> objects_by_owner;
    uint32_t                                        life_cycle_id = DEFAULT_LIFE_CYCLE_ID;
    if (m_life_cycle_ids.find(position) != m_life_cycle_ids.end()) {
        life_cycle_id = m_life_cycle_ids[position] + 1;
    }
    std::vector<std::string> owners;
    for (auto& parsed_object_ptr : parsed_objects) {
        if (parsed_object_ptr->class_name == "Connector" &&
            parsed_object_ptr->object_props.contains(SYSTEM_ID_KEY)) {
            // SystemId转成整型
            auto system_id = parsed_object_ptr->object_props[SYSTEM_ID_KEY].as_int64();
            parsed_object_ptr->object_props[SYSTEM_ID_KEY] = system_id;
        }
        object_info_t object_info;
        object_info.ClassName     = parsed_object_ptr->class_name;
        object_info.ObjectName    = parsed_object_ptr->object_name;
        object_info.ObjectProps   = mc::json::json_encode(parsed_object_ptr->object_props);
        object_info.ObjectExtends = mc::json::json_encode(parsed_object_ptr->extra_props);
        if (objects_by_owner.find(parsed_object_ptr->app_name) == objects_by_owner.end()) {
            objects_by_owner[parsed_object_ptr->app_name].Position    = position;
            objects_by_owner[parsed_object_ptr->app_name].LifeCycleId = life_cycle_id;
            owners.push_back(parsed_object_ptr->app_name);
        }
        objects_by_owner[parsed_object_ptr->app_name].Objects.push_back(object_info);
    }

    for (auto& parsed_func_object_ptr : parsed_func_objects) {
        for (auto& app_name : parsed_func_object_ptr->app_names) {
            object_info_t object_info;
            object_info.ClassName     = parsed_func_object_ptr->class_name;
            object_info.ObjectName    = parsed_func_object_ptr->object_name;
            object_info.ObjectProps   = mc::json::json_encode(parsed_func_object_ptr->object_props);
            object_info.ObjectExtends = "{}"; // 函数对象通常没有扩展属性
            if (objects_by_owner.find(app_name) == objects_by_owner.end()) {
                objects_by_owner[app_name].Position    = position;
                objects_by_owner[app_name].LifeCycleId = life_cycle_id;
                owners.push_back(app_name);
            }
            objects_by_owner[app_name].Objects.push_back(object_info);
        }
    }
    m_objects[position]            = objects_by_owner;
    m_life_cycle_ids[position]     = life_cycle_id;
    m_owners_by_position[position] = owners;
}

mc::dict object_group_data::get_topology(std::string position) {
    std::lock_guard<std::mutex> lock(m_topology_mutex);
    auto                        topology_it = m_topology_by_position.find(position);
    if (topology_it == m_topology_by_position.end()) {
        return {};
    }
    return topology_it->second;
}

void object_group_data::save_topology(std::string position, const mc::dict& topology) {
    std::lock_guard<std::mutex> lock(m_topology_mutex);
    m_topology_by_position[position] = topology;
}

void object_group_data::save_device_object(mc::engine::abstract_object* object,
                                           std::string position, std::string object_name) {
    std::lock_guard<std::mutex> lock(m_device_objects_mutex);
    m_device_objects[position][object_name] = object;
}

void object_group_data::clear_device_objects(std::string position) {
    std::lock_guard<std::mutex> lock(m_device_objects_mutex);
    m_device_objects.erase(position);
}

bool object_group_data::is_device_object(std::string position, std::string object_name) {
    return get_device_object(position, object_name) != nullptr;
}

mc::engine::abstract_object* object_group_data::get_device_object(std::string position,
                                                                  std::string object_name) {
    std::lock_guard<std::mutex> lock(m_device_objects_mutex);
    auto                        position_it = m_device_objects.find(position);
    if (position_it == m_device_objects.end()) {
        return nullptr;
    }
    auto object_it = position_it->second.find(object_name);
    if (object_it == position_it->second.end()) {
        return nullptr;
    }
    return object_it->second;
}

std::vector<std::string> object_group_data::get_owners(std::string position) {
    return m_owners_by_position[position];
}

} // namespace devmon

MC_REFLECT(devmon::object_info_t,
           ((ClassName, "ClassName"))((ObjectName, "ObjectName"))((ObjectProps, "ObjectProps"))(
               (ObjectExtends, "ObjectExtends")))

MC_REFLECT(devmon::object_group_t,
           ((Position, "Position"))((Objects, "Objects"))((LifeCycleId, "LifeCycleId")))
